package com.coder.rental.security;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.core.date.DateTime;
import cn.hutool.jwt.JWTPayload;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.coder.rental.entity.User;
import com.coder.rental.utils.JwtUtils;
import com.coder.rental.utils.RedisUtils;
import com.coder.rental.utils.ResultCode;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: zhanglin
 * @description:    登录认证成功处理方法
 * @date 2025/3/22
 */
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    //redis工具类
    @Resource
    private RedisUtils redisUtils;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        //设置客户端响应的数据类型是json
        response.setContentType("application/json;charset=utf-8");
        //从authentication认证信息中使用getPrincipal()方法获取用户的信息，返回User类型对象
        User user = (User) authentication.getPrincipal();
        /*
        * 准备token相关数据并生成json
        * 生成token处理，把结果转为json，再通过io流的方式将结果输入
        * */
        Map<String, Object> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("userid", user.getId());
        String token = JwtUtils.createToken(map);//createToken方法里已经设置了过期时间
        //解析token并获取过期时间，parseToken是解析token，getClaim是从JWTPayload载荷中获取token过期时间EXPIRES_AT
        NumberWithFormat claim = (NumberWithFormat) JwtUtils.parseToken(token).getClaim(JWTPayload.EXPIRES_AT);
        long expireTime = Convert.toDate(claim).getTime();//对过期时间进行转换为Date
        /*
        * 构造认证结果对象，(用户id,成功状态码,token,过期时间)
        * */
        AuthenticationResult authenticationResult =
                new AuthenticationResult(user.getId(), ResultCode.SUCCESS, token, expireTime);
        //获取一个结果，将User对象序列化为JSON数据，并在序列化中禁用循环引用检测，避免在序列化中出现无限循环引用
        String result = JSON.toJSONString(authenticationResult, SerializerFeature.DisableCircularReferenceDetect);
        //获取输出流，向客户端发送认证结果
        ServletOutputStream outputStream = response.getOutputStream();
        //设置字符编码
        outputStream.write(result.getBytes(StandardCharsets.UTF_8));
        //flush()方法将数据发送出去，避免数据滞留在缓冲区
        outputStream.flush();
        //关闭io流
        outputStream.close();
        /*
        * 将token存入redis缓存中，并设置过期时间
        * expireTime和nowTime是毫秒，/1000计算秒
        * */
        String tokenKey = "token:"+token;
        long nowTime = DateTime.now().getTime();
        redisUtils.set(tokenKey, token, (expireTime - nowTime)/1000);
    }
}
